Atmospheric Temperature vs Niño#

https://www.ncei.noaa.gov/data/global-historical-climatology-network-daily/doc/GHCND_documentation.pdf

import warnings
warnings.filterwarnings("ignore")
import os
import sys

import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
import df2img


sys.path.append("../../../../indicators_setup")
from ind_setup.colors import get_df_col, plotting_style
from ind_setup.tables import plot_df_table

plotting_style()
from ind_setup.core import fontsize

sys.path.append("../../../functions")
from data_downloaders import GHCN, download_oni_index

Define location and variables of interest#

country = 'Palau'
vars_interest = ['tmax', 'TMAX']

Get Data#

df_country = GHCN.get_country_code(country)
print(f'The GHCN code for {country} is {df_country["Code"].values[0]}')
The GHCN code for Palau is PS
df_stations = GHCN.download_stations_info()
df_country_stations = df_stations[df_stations['ID'].str.startswith(df_country.Code.values[0])]
print(f'There are {df_country_stations.shape[0]} stations in {country}')
There are 13 stations in Palau
GHCND_dir = 'https://www.ncei.noaa.gov/data/global-historical-climatology-network-daily/access/'

Using Koror Station#

Analysis of how much the maximum and minimum temperatures over time are changing.
The analysis of the difference between these 2 variables will allow us to know how the daily variability is being modified

id = 'PSW00040309' # Koror Station
dict_min = GHCN.extract_dict_data_var(GHCND_dir, 'TMIN', df_country_stations.loc[df_country_stations['ID'] == id])[0][0]
dict_max = GHCN.extract_dict_data_var(GHCND_dir, 'TMAX', df_country_stations.loc[df_country_stations['ID'] == id])[0][0]
st_data = pd.concat([dict_min['data'], (dict_max['data'])], axis=1).dropna()
st_data['TMIN'] = np.where(st_data['TMIN'] >50, np.nan, st_data['TMIN'])

ONI index#

https://origin.cpc.ncep.noaa.gov/products/analysis_monitoring/ensostuff/ONI_v5.php

p_data = 'https://psl.noaa.gov/data/correlation/oni.data'
df1 = download_oni_index(p_data)
import plotly.graph_objects as go
import pandas as pd

# Assume df1 and get_df_col() are predefined
lims = [-.5, .5]

# Create the base figure
fig = go.Figure()

# Add the main line plot
fig.add_trace(go.Scatter(
    x=df1.index,
    y=df1['ONI'],
    mode='lines',
    line=dict(color=get_df_col()[0], width=2),
    name='ONI'
))

# Add horizontal lines (Thresholds)
hline_colors = ['grey', get_df_col()[1], 'grey']
hline_labels = ['Threshold', 'Zero Line', 'Threshold']
for i, y in enumerate([lims[0], 0, lims[1]]):
    fig.add_trace(go.Scatter(
        x=[df1.index[0], df1.index[-1]],
        y=[y, y],
        mode='lines',
        line=dict(color=hline_colors[i], dash='dash'),
        name=hline_labels[i],
        showlegend=(i == 0)  # Only show legend once
    ))

# Add fill for values greater than lims[1]
fig.add_trace(go.Scatter(
    x=list(df1.index) + list(df1.index[::-1]),
    y=list(df1['ONI'].where(df1['ONI'] > lims[1], lims[1])) + [lims[1]] * len(df1),
    fill='toself',
    mode='none',
    fillcolor=get_df_col()[2],
    opacity=0.7,
    name='Above Threshold'
))

# Add fill for values less than lims[0]
fig.add_trace(go.Scatter(
    x=list(df1.index) + list(df1.index[::-1]),
    y=[lims[0]] * len(df1) + list(df1['ONI'].where(df1['ONI'] < lims[0], lims[0]))[::-1],
    fill='toself',
    mode='none',
    fillcolor=get_df_col()[3],
    opacity=0.7,
    name='Below Threshold'
))

# Update layout
fig.update_layout(
    title='ONI Index Plot',
    xaxis_title='Time',
    yaxis_title='ONI Index',
    font=dict(size=fontsize),
    legend=dict(orientation='h', y=-0.2),
    plot_bgcolor='white',
    margin=dict(l=50, r=50, t=50, b=50),
)

# Show the plot
fig.show()
fig, ax = plt.subplots(figsize = [15, 6])
df1.plot(ax = ax, color = get_df_col()[0], lw = 2)
ax.hlines([lims[0], 0, lims[1]], df1.index[0], df1.index[-1], 
          color = ['grey', get_df_col()[1], 'grey', get_df_col()[1]], 
          linestyle = '--', label = 'Thresholds')

ax.fill_between(df1.index, lims[1], df1.ONI, where = (df1.ONI > lims[1]), color = get_df_col()[2], alpha = 0.7)
ax.fill_between(df1.index, df1.ONI, lims[0], where = (df1.ONI < lims[0]), color = get_df_col()[3], alpha = 0.7)

ax.legend(fontsize = fontsize, ncol = 2)
ax.set_ylabel('ONI Index', fontsize = fontsize)
Text(0, 0.5, 'ONI Index')
../../../_images/d37d143d45d781b2b6473e3a90df547e4b25b97f7da679793610fa3e4820f830.png
st_data_monthly = st_data.resample('M').mean()
st_data_monthly.index = pd.DatetimeIndex(st_data_monthly.index).to_period('M').to_timestamp() + pd.offsets.MonthBegin(1)
st_data_monthly
TMIN TMAX
DATE
1951-08-01 23.967742 31.045161
1951-09-01 23.993548 30.345161
1951-10-01 23.990000 30.673333
1951-11-01 24.509677 31.425806
1951-12-01 24.360000 31.023333
... ... ...
2024-09-01 25.900000 30.477419
2024-10-01 25.846667 29.926667
2024-11-01 25.855172 30.382759
2024-12-01 25.780952 30.185714
2025-01-01 25.925000 30.787500

882 rows × 2 columns

rolling_mean = 6 #months
df1['tmin'] = st_data_monthly['TMIN'].rolling(window=rolling_mean).mean()
df1['tmax'] = st_data_monthly['TMAX'].rolling(window=rolling_mean).mean()
df1['tdiff'] = df1['tmax'] - df1['tmin']
df1['tmean'] = (df1['tmax'] + df1['tmin'])/2
fig, ax = plt.subplots(figsize = [15, 6])
df1.ONI.plot(ax = ax, color = get_df_col()[0], lw = 2)

ax2 = ax.twinx()
df1.tmin.plot(ax = ax2, color = get_df_col()[1], lw = 2)
# df1.tmax.plot(ax = ax2, color = get_df_col()[1], lw = 2)
# df1.tdiff.plot(ax = ax2, color = get_df_col()[1], lw = 2)
# df1.tmean.plot(ax = ax2, color = get_df_col()[1], lw = 2)
<Axes: >
../../../_images/38ca4f4bee0418c70674d54550d115e62da6910be8d5e2ff4ae10174e6e68d20.png
low_lim = np.nanmin(df1.tmin)

fig, ax = plt.subplots(figsize = [15, 6])
df1.tmin.plot(ax = ax, color = get_df_col()[1], lw = 2)

ax.fill_between(df1.index, low_lim, df1.tmin, where = (df1.ONI > lims[1]), color = get_df_col()[2],
                 alpha = 0.7, label = f'ONI over th: {lims[1]}')
ax.fill_between(df1.index, low_lim, df1.tmin, where = (df1.ONI < lims[0]), color = get_df_col()[3], 
                alpha = 0.7, label = f'ONI below th: {lims[0]}')

ax.fill_between(df1.index, low_lim, df1.tmin, where = ((df1.ONI > lims[0]) & (df1.ONI < lims[1])),
                 color = get_df_col()[6], alpha = 0.075)

ax.legend(fontsize=fontsize)
ax.set_title('TMIN and ONI', fontsize = fontsize)
ax.set_ylabel('TMIN [°C]', fontsize = fontsize)
ax.set_xlabel('Time', fontsize = fontsize)
Text(0.5, 0, 'Time')
../../../_images/a8ba322e81b6b2f7b1b7e4638788b6f1b208041b9075a5427d103215d3e3c412.png
low_lim = np.nanmin(df1.tmax)
fig, ax = plt.subplots(figsize = [15, 6])
df1.tmax.plot(ax = ax, color = get_df_col()[1], lw = 2)

ax.fill_between(df1.index, low_lim, df1.tmax, where = (df1.ONI > lims[1]), color = get_df_col()[2],
                 alpha = 0.7, label = f'ONI over th: {lims[1]}')
ax.fill_between(df1.index, low_lim, df1.tmax, where = (df1.ONI < lims[0]), color = get_df_col()[3], 
                alpha = 0.7, label = f'ONI below th: {lims[0]}')

ax.fill_between(df1.index, low_lim, df1.tmax, where = ((df1.ONI > lims[0]) & (df1.ONI < lims[1])),
                 color = get_df_col()[6], alpha = 0.075)

ax.legend(fontsize = fontsize)
ax.set_title('TMAX and ONI', fontsize = fontsize)
ax.set_ylabel('TMAX [°C]', fontsize = fontsize)
ax.set_xlabel('Time', fontsize = fontsize)
Text(0.5, 0, 'Time')
../../../_images/3e279e779f2e6a4face922239e8601fe60f94fa2993e85964e502ab542fef54f.png
plt.figure(figsize=(5, 4))
sns.heatmap(df1.corr(), annot=True, cmap='coolwarm', vmin=-1, vmax=1)
plt.title('Correlation Heatmap')
plt.show()
../../../_images/d75b729d5b4254d73832f08e6c2690be5d7e647c65529ae336da0ff4a2fec3a3.png
df_format = np.round(df1.describe(), 2)
fig = plot_df_table(df_format)
df2img.save_dataframe(fig=fig, filename="getting_started.png")
../../../_images/b4ac6de41652fe853af19588abf7f6e2decb0ded65ca4c0bbad2018cf62d7f4e.png